using System.Text;
using DiLu.Expansion.DataStorage.Common.Columns;
using DiLu.Expansion.DataStorage.Common.Contracts;
using DiLu.Expansion.Serilog;
using SqlSugar;
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace DiLu.Expansion.DataStorage.SqlSugarExpansion;

public static class SqlSugarExpansionAop
{
    /// <summary>
    ///     添加SQL执行前的事件，用于在SQL执行前记录日志
    /// </summary>
    /// <param name="sugarScopeProvider">提供线程安全操作的SqlSugar客户端对象</param>
    /// <param name="configId">数据库名称</param>
    public static void AddLogExecuting(this SqlSugarScopeProvider sugarScopeProvider, string configId)
    {
        //SQL执行前事件。可在这里查看生成的sql
        sugarScopeProvider.Aop.OnLogExecuting = (sql, pars) =>
        {
            var tempSql = LookSql(sql, pars);
            DoSerilog.GetDebugLogger().Debug("{DBName}连接 运行SQL：{TempSql}", configId, tempSql);
        };
    }

    /// <summary>
    ///     查看赋值后的sql
    /// </summary>
    /// <param name="sql">原始的SQL语句</param>
    /// <param name="pars">参数集合，包含SQL语句中使用的参数及其值</param>
    /// <returns>返回替换参数后的SQL字符串</returns>
    private static string LookSql(string sql, IReadOnlyCollection<SugarParameter> pars)
    {
        if (pars == null || pars.Count == 0) return sql;

        var sbSql = new StringBuilder(sql);
        var tempOrderPars = pars.Where(p => p.Value != null)
            .OrderByDescending(p => p.ParameterName.Length).ToList();
        foreach (var temp in tempOrderPars) sbSql.Replace(temp.ParameterName, $"'{temp.Value}'");

        return sbSql.ToString();
    }

    /// <summary>
    ///     添加SQL执行后的事件，用于在SQL执行后记录日志
    /// </summary>
    /// <param name="sugarScopeProvider">提供线程安全操作的SqlSugar客户端对象</param>
    /// <param name="recordLogSecond">记录日志秒数</param>
    public static void AddLogExecuted(this SqlSugarScopeProvider sugarScopeProvider, int recordLogSecond)
    {
        //获取执行后的总毫秒数
        sugarScopeProvider.Aop.OnLogExecuted = (sql, pars) =>
        {
#if DEBUG
            var sqlExecutionTotalMilliseconds = sugarScopeProvider.Ado.SqlExecutionTime.TotalMilliseconds;
            DoSerilog.GetDebugLogger().Debug("sql执行时间：{SqlExecutionTotalMilliseconds} ms", sqlExecutionTotalMilliseconds);
#endif
            var totalSeconds = sugarScopeProvider.Ado.SqlExecutionTime.TotalSeconds;
            if (totalSeconds < recordLogSecond) return;
            var tempSql = LookSql(sql, pars);
            //记录超过x秒的日志方法
            var configId = sugarScopeProvider.CurrentConnectionConfig.ConfigId;
            DoSerilog.GetFileLogger("LongRunSql")
                .Warning("{ConfigId}--{TempSql}--sql执行时间：{TotalSeconds} s", configId, tempSql, totalSeconds);
        };
    }

    /// <summary>
    ///     添加SQL执行后错误事件，用于在SQL执行出现错误时记录日志
    /// </summary>
    /// <param name="sugarScopeProvider">提供线程安全操作的SqlSugar客户端对象</param>
    public static void AddSqlErrorLog(this SqlSugarScopeProvider sugarScopeProvider)
    {
        //执行SQL 错误事件
        sugarScopeProvider.Aop.OnError = exp =>
        {
            var sugarParameterStr = new StringBuilder("###SugarParameter参数为:");
            var parameters = exp.Parametres as SugarParameter[];
            if (parameters != null) sugarParameterStr.Append(JsonSerializer.Serialize(parameters));

            var errors = new StringBuilder();
            errors.AppendLine($"SqlSugarClient执行sql异常信息:{exp.Message}");
            errors.AppendLine($"###赋值后sql:{LookSql(exp.Sql, parameters)}");
            //记录日志
#if DEBUG
            DoSerilog.GetDebugLogger().Debug("{Errors}", errors);
#else
            DoSerilog.GetFileLogger("ErrorSql").Error("{Errors}", errors);
#endif
        };
    }

    /// <summary>
    ///     添加数据过滤器，在SQL执行前对数据进行过滤
    /// </summary>
    /// <typeparam name="TOperateAccountId">泛型参数，通常用于指定与操作相关的实体类型</typeparam>
    /// <param name="sugarScopeProvider">提供线程安全操作的SqlSugar客户端对象</param>
    /// <param name="dataExecutingAction">用于定义数据过滤逻辑的委托，接收当前上下文和数据过滤模型作为参数，默认为null时使用内部预设的数据过滤逻辑</param>
    public static void AddDataExecuting<TOperateAccountId>(this SqlSugarScopeProvider sugarScopeProvider,
        Action<object, DataFilterModel> dataExecutingAction = null)
    {
        if (dataExecutingAction == null)
        {
            sugarScopeProvider.Aop.DataExecuting = (oldValue, entityInfo) =>
            {
                if (entityInfo.PropertyName != nameof(IHasCreatorId<TOperateAccountId>.CreatorId) &&
                    entityInfo.PropertyName != nameof(IHasLastModifierId<TOperateAccountId>.LastModifierId)) return;
                if (!EqualityComparer<TOperateAccountId>.Default.Equals((TOperateAccountId)oldValue, default)) return;
                entityInfo.SetValue(DataStorageOperateContract<TOperateAccountId>.OperateAccountId.Value);
            };
            return;
        }

        sugarScopeProvider.Aop.DataExecuting = dataExecutingAction;
    }
}